#include "napi/native_api.h"
#include <js_native_api.h>
#include <hilog/log.h>
#include <cstring>
#include "native_callback.h"

// ArkTS 回调函数的引用
static napi_ref g_callbackRef = nullptr;

// C++ 回调函数 - 这个函数会被业务层调用
napi_value nativeCallback(napi_env env, const char* result) {
    if (g_callbackRef == nullptr) {
        OH_LOG_ERROR(LOG_APP, "回调函数未注册");
        return nullptr;
    }
    
    // 获取保存的回调函数
    napi_value callback;
    napi_get_reference_value(env, g_callbackRef, &callback);
    
    // 准备调用参数
    napi_value argv[1];
    napi_create_string_utf8(env, result, NAPI_AUTO_LENGTH, &argv[0]);
    
    napi_value global;
    napi_get_global(env, &global);
    
    // 调用 ArkTS 回调函数
    napi_value callResult;
    napi_status status = napi_call_function(env, global, callback, 1, argv, &callResult);
    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "调用回调函数失败");
        return nullptr;
    }
    
    // 清理回调引用
    napi_delete_reference(env, g_callbackRef);
    g_callbackRef = nullptr;
    
    return callResult;
}

/**
 * NAPI 导出函数 - ArkTS 调用的入口
 * @param env
 * @param info
 * @return
 */
static napi_value callFuncC(napi_env env, napi_callback_info info)
{
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, LOG_TAG, "开始调用C++函数");
    // 1. 获取参数
    size_t argc = 2;
    napi_value args[2];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    // 2. 检查参数数量
    if (argc < 2) {
        napi_throw_error(env, nullptr, "需要两个参数: 输入字符串和回调函数");
        return nullptr;
    }
    
    // 验证回调函数类型
    napi_valuetype callbackType;
    napi_typeof(env, args[1], &callbackType);
    if (callbackType != napi_function) {
        napi_throw_error(env, nullptr, "第二个参数必须是函数");
        return nullptr;
    }
    
    // 3. 解析输入参数
    char input[256] = {0};
    size_t input_length;
    napi_get_value_string_utf8(env, args[0], input, sizeof(input), &input_length);
    
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, LOG_TAG, "输入字符串: %{public}s", input);
    
    // 保存回调函数引用，创建函数引用（防止被垃圾回收）
    napi_create_reference(env, args[1], 1, &g_callbackRef);
    
    // 调用 C++ 业务函数
    processBusinessAsync(env, input, nativeCallback);
    
    napi_value undefined;
    napi_get_undefined(env, &undefined);
    return undefined;
}



/**
 * EXTERN_C_START 和 EXTERN_C_END：
    这两个宏用于确保中间的代码以C语言的方式编译和链接，防止C++的名称修饰（name mangling）。这样，在动态链接时能够正确找到函数。
 * 这是 C++ 的兼容性宏，确保代码在 C 和 C++ 环境下都能正确编译
 * 防止 C++ 的 name mangling（名称修饰）
 * 
 * Init 函数：
 *      这是一个模块初始化函数，在模块被加载时调用
 * Init函数作用：批量定义属性到 exports 对象，用于初始化插件，用于将ArkTS层的函数或属性与C++层的函数进行关联
 * 
 * @param env       N-API环境句柄
 * @param exports   表示模块导出对象的napi_value，相当于ArkTS中的exports对象
 * @return
 */
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    // 定义了一个napi_property_descriptor数组desc，每个元素描述一个导出属性（这里导出两个函数：add和multiply）
    napi_property_descriptor desc[] = {
        {
            "callFuncC",           // 属性名（在 ArkTS 中调用的名称）
            nullptr,         // 属性名（napi_value 形式，这里用字符串代替）
            callFuncC,             // 原生函数指针
            nullptr,         // getter 函数
            nullptr,         // setter 函数
            nullptr,         // 属性值
            napi_default,    // 属性特性
            nullptr          // 用户数据
        }
        
    };
    // sizeof(desc) / sizeof(desc[0]) 计算数组元素个数
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

// 这是一个napi_module类型的静态变量，用于描述模块信息
static napi_module myNativeModule = {
    .nm_version = 1,            // N-API模块版本，这里为1
    .nm_flags = 0,              // 模块标志，0表示没有特殊标志
    .nm_filename = nullptr,     // 模块文件名，这里为nullptr，表示使用默认文件名
    .nm_register_func = Init,   // 模块注册函数，这里指定为Init函数
    .nm_modname = "callback",      // 模块名，这里为"my"，在ArkTS中导入模块时使用
    .nm_priv = ((void*)0),      // 私有数据，这里为nullptr
    .reserved = { 0 },          // 保留字段，初始化为0
};

/**
 * 模块注册函数
 * 
 * 关键特性解析：
 *  extern "C"
 *      指定函数使用 C 语言的链接约定
 *      防止 C++ 编译器进行名称修饰
 *  __attribute__((constructor))
 *      GCC/Clang 编译器特性
 *      标记函数在程序加载时自动执行（在 main 函数之前）
 *      相当于静态构造函数
 *  napi_module_register(&demoModule)
 *      核心注册函数：向鸿蒙运行时注册原生模块
 *      参数：指向 napi_module 结构体的指针
 * 
 * 整个流程是：
 *      当动态库被加载时，RegisterEntryModule函数自动执行，注册demoModule模块。
 *      当ArkTS层导入这个模块时，会调用Init函数，将add和multiply函数绑定到 exports 对象上，这样ArkTS层就可以调用这两个原生函数了。
 */
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&myNativeModule);
}